﻿using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;

namespace WebCam
{
    class Program
    {
        static void Main(string[] args)
        {
        }
    }
    public class WebCam
    {
        private const int WM_USER = 0x400;
        private const int WS_CHILD = 0x40000000;
        private const int WS_VISIBLE = 0x10000000;
        private const int WM_CAP_START = WM_USER;
        private const int WM_CAP_STOP = WM_CAP_START + 68;
        private const int WM_CAP_DRIVER_CONNECT = WM_CAP_START + 10;
        private const int WM_CAP_DRIVER_DISCONNECT = WM_CAP_START + 11;
        private const int WM_CAP_SAVEDIB = WM_CAP_START + 25;
        private const int WM_CAP_GRAB_FRAME = WM_CAP_START + 60;
        private const int WM_CAP_SEQUENCE = WM_CAP_START + 62;
        private const int WM_CAP_FILE_SET_CAPTURE_FILEA = WM_CAP_START + 20;
        private const int WM_CAP_SEQUENCE_NOFILE = WM_CAP_START + 63;
        private const int WM_CAP_SET_OVERLAY = WM_CAP_START + 51;
        private const int WM_CAP_SET_PREVIEW = WM_CAP_START + 50;
        private const int WM_CAP_SET_CALLBACK_VIDEOSTREAM = WM_CAP_START + 6;
        private const int WM_CAP_SET_CALLBACK_ERROR = WM_CAP_START + 2;
        private const int WM_CAP_SET_CALLBACK_STATUSA = WM_CAP_START + 3;
        private const int WM_CAP_SET_CALLBACK_FRAME = WM_CAP_START + 5;
        private const int WM_CAP_SET_SCALE = WM_CAP_START + 53;
        private const int WM_CAP_SET_PREVIEWRATE = WM_CAP_START + 52; 
        
        public IntPtr Handle { get; private set; }
        public int X { get; private set; }
        public int Y { get; set; }
        public int Width { get; set; }
        public int Height { get; set; }
        
        private readonly List<object> _isRunning = new List<object>();
        public bool IsRunning { get { return _isRunning.Count > 0; } }

        private IntPtr _hwnd;

        public WebCam(IntPtr handle, int x, int y, int width, int height)
        {
            Handle = handle;
            X = x;
            Y = y;
            Width = width;
            Height = height;
        }

        [DllImport("avicap32.dll")]
        private static extern IntPtr capCreateCaptureWindowA(byte[] lpszWindowName, int dwStyle, int x, int y, int nWidth, int nHeight, IntPtr hWndParent, int nId);
        [DllImport("avicap32.dll")]
        private static extern int capGetVideoFormat(IntPtr hWnd, IntPtr psVideoFormat, int wSize);
        [DllImport("User32.dll")]
        private static extern bool SendMessage(IntPtr hWnd, int wMsg, int wParam, long lParam);

        public bool Start()
        {
            if (IsRunning) return false;
            _isRunning.Add(1);
            var name = new byte[256];
            _hwnd = capCreateCaptureWindowA(name, WS_CHILD | WS_VISIBLE, X, Y, Width, Height, Handle, 0);
            if (_hwnd == IntPtr.Zero)
            {
                _isRunning.Clear();
                return false;
            }
            SendMessage(_hwnd, WM_CAP_SET_CALLBACK_VIDEOSTREAM, 0, 0);
            SendMessage(_hwnd, WM_CAP_SET_CALLBACK_STATUSA, 0, 0);
            SendMessage(_hwnd, WM_CAP_DRIVER_CONNECT, 0, 0);
            SendMessage(_hwnd, WM_CAP_SET_SCALE, 1, 0);
            SendMessage(_hwnd, WM_CAP_SET_PREVIEWRATE, 66, 0);
            SendMessage(_hwnd, WM_CAP_SET_OVERLAY, 1, 0);
            SendMessage(_hwnd, WM_CAP_SET_PREVIEW, 1, 0);
            return true;
        }
        public void Stop()
        {
            if (IsRunning) return;
            SendMessage(_hwnd, WM_CAP_DRIVER_DISCONNECT, 0, 0);
            _isRunning.Clear();
        }
        public void Snapshot(string path)
        {
            if (!IsRunning) throw new InvalidOperationException();
            var fn = Marshal.StringToHGlobalAnsi(path);
            SendMessage(_hwnd, WM_CAP_SAVEDIB, 0, fn.ToInt64());
        }
        public void Record(string path)
        {
            if (!IsRunning) throw new InvalidOperationException();
            var fn = Marshal.StringToHGlobalAnsi(path);
            SendMessage(_hwnd, WM_CAP_FILE_SET_CAPTURE_FILEA, 0, fn.ToInt64());
            SendMessage(_hwnd, WM_CAP_SEQUENCE, 0, 0);
        }
        public void StopRecord()
        {
            if (!IsRunning) throw new InvalidOperationException();
            SendMessage(_hwnd, WM_CAP_STOP, 0, 0);
        }
    }
}
